package edu.kit.csl.pisa.models;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import edu.kit.csl.pisa.datatypes.AlignmentPosition;
import edu.kit.csl.pisa.datatypes.AlignmentSourceWord;
import edu.kit.csl.pisa.datatypes.Corpus;
import edu.kit.csl.pisa.datatypes.Dictionary;
import edu.kit.csl.pisa.datatypes.SentenceAlignment;
import edu.kit.csl.pisa.io.Logger;
import edu.kit.csl.pisa.ui.Configuration;

/**
 * This class encapsulates the Model *P models. In-depth discussion
 * of this model can be found in the technical paper. The parameters are stored
 * as n-dimensional double arrays. The order in which the dimensions are
 * assigned is as follows:<br>
 * At first, the parameters on the right side of the "|" in the order in which 
 * these dependencies are denoted in the technical paper. The last dimension
 * will always store the left side of the "|" (in P(.|.) ):<br>
 * n:	1. English Word (e_A_0j)<br>
 * 		2. Fertility (f_j)<br>
 * t:	1. English Word (e_A_0j)<br>
 * 		2. Relative French Phoneme Position (A_4j)<br>
 * 		3. French Phoneme (f_j)<br>
 * Note that since the distortion model between Model 3P and 4P differs, the d
 * parameter is to be defined by implementing subclasses. Same for o because it
 * may be changed in the future
 */
abstract public class ModelP extends AlignmentModel {
	
	/*
	 * Model parameter. Which dimension contains which values is denoted in the
	 * head of this class.
	 */
	protected double[][] n;
	protected double[][][] t;
	protected double p1, p0;
	
	// Configuration parameters
	protected final int maxFertility;
	protected final int maxWordLength;

	/**
	 * Sole constructor.
	 * 
	 * @param name name of the model
	 */
	public ModelP(String name) {
		super(name);
		p0 = Math.log(0.999f);
		p1 = Math.log(0.001f);
		maxFertility = Configuration.getSingleton().getInteger(
				"maxFertility") + 1;
		maxWordLength = Configuration.getSingleton()
				.getInteger("maxWordLength") + 1;
	}
	
	/**
	 * This method can be invoked by implementing subclasses to aggregate 
	 * fractional counts for the n and t parameters.
	 * 
	 * @param model model instance for collecting fractional counts
	 * @param pair sentence alignment
	 * @param weight weight of the alignment
	 * @see #collectFractionalCounts(Corpus)
	 */
	protected void aggregateModelPFractionalCount(ModelP model, 
			SentenceAlignment a, double weight) {
		synchronized (model.n) {
			for (AlignmentSourceWord word : a.eachSourceWordWithoutNull()) {
				model.n	[word.getSrcWord()]
							[word.getFertility()] += weight;
			}
		}
		synchronized (model.t) {
			for (AlignmentPosition pos : a.eachTargetPhoneme()) {
				model.t	[pos.getSourceWord()]
							[pos.getTargetWordPhonemePosition()]
							[pos.getTargetPhoneme()] += weight;
			}
		}
	}
	
	/**
	 * This method can be used by implementing subclasses to calculate the
	 * the logarithm of P(a,f|e) for the p0, p1, t, and n parameters. The
	 * summands are written into the given array and the last index is returned
	 * 
	 * @param summands array to write values to
	 * @param a
	 * @return the index of the first free entry in {@code summands}
	 * @see #calculateAlignmentProbability(SentenceAlignment)
	 */
	protected int calculateModelPAlignmentProbability(double[] summands,
			SentenceAlignment a) {
		final int nTrgtWords = a.getAlignment().countTargetWords(); // k
		final byte fert0 = a.getAlignment().getNullFertility();
		int i = 0;
		// p0, p1
		summands[i++] = p0 * (nTrgtWords - 2 * fert0);
		summands[i++] = p1 * fert0;
		// Fertility
		summands[i++] = binomSpecial(nTrgtWords, fert0);
		for (AlignmentSourceWord word : a.eachSourceWordWithoutNull()) {
			summands[i++] = n[word.getSrcWord()][word.getFertility()];
			summands[i++] = logFacFert[word.getFertility()];
		}
		// Lexical translation
		for (AlignmentPosition pos : a.eachTargetPhoneme()) {
			summands[i++] = t[pos.getSourceWord()]
							 [pos.getTargetWordPhonemePosition()]
							 [pos.getTargetPhoneme()];
		}
		return i;
	}
	
	/**
	 * Allocate memory for model P parameters.
	 * 
	 * @param print Set to {@code true} to print parameter sizes on the screen
	 */
	protected void allocateModelPMemory(boolean print) {
		final int srcDictSize = 
				Dictionary.getSingleton(Dictionary.SRC_DICT).size() + 1;
		final int trgtDictSize = 
				Dictionary.getSingleton(Dictionary.TRGT_DICT).size() + 1;
		Logger log = Logger.getSingleton();
		// Allocate memory
		if (print) {
			log.debug("Model-P n parameter: " + AlignmentModel
					.humanReadableDoubleCount(srcDictSize * maxFertility));
			log.debug("Model-P t parameter: " + AlignmentModel
					.humanReadableDoubleCount(srcDictSize * maxWordLength
							* trgtDictSize));
		}
		n = new double[srcDictSize][maxFertility];
		t = new double[srcDictSize][maxWordLength][trgtDictSize];
	}
	
	
	/**
	 * Set p1 parameter.
	 * 
	 * @param p1 new value for p1
	 */
	public void setP1(double p1) {
		//this.p1 = p1;
		//p0 = Math.log(1 - Math.exp(p1));
	}
}
